#include "TurbineSimulator.h"
#include "collisionBase.h"

llib::Boolean TurbineSimulator::Render()
{
    /* animation time stuff */
    Float animationTime = 0.0;
    if(!m_pause) {
        Float curTime = Float(m_timer.elapsed()) + m_pausedTime;
        if(curTime - m_lastTime > m_timeDelta) {
            m_stepId += 1;
            animationTime = m_timeDelta * m_stepId;
            m_lastTime = curTime;
            m_pausedTime = 0.f;
        } else {
            animationTime = m_timeDelta * m_stepId;
            animationTime += curTime - m_lastTime;
        }
    } else {
        animationTime = m_timeDelta * m_stepId + m_pausedTime;
    }

    /* model stuff */
    /* 1. update model */
    m_leftTurbine.update(animationTime);
    m_rightTurbine.update(animationTime);
    /* 2. collision chech stuff */
    FOR(tid,0,SZ(m_leftTurbine.TriCollision)) m_leftTurbine.TriCollision[tid] = FALSE;
    FOR(tid,0,SZ(m_rightTurbine.TriCollision)) m_rightTurbine.TriCollision[tid] = FALSE;
    FOR(leftId,0,SZ(m_leftTurbine.TriList)) {
        Triangle3f &triL = m_leftTurbine.TriList[leftId];
        FOR(rightId,0,SZ(m_rightTurbine.TriList)) {
            Triangle3f &triR = m_rightTurbine.TriList[rightId];
            if( TrianglePairTester::TestStatic(triL,triR) ) {
                m_leftTurbine.TriCollision[leftId] = TRUE;
                m_rightTurbine.TriCollision[rightId] = TRUE;
            }
        }
    }

    /* view point stuff */
    Point3f viewPoint;
    viewPoint.x = m_viewPointDistance * sin( m_viewPointAngle / 180.0 * PI );
    viewPoint.y = m_viewPointHeight;
    viewPoint.z = m_viewPointDistance * cos( m_viewPointAngle / 180.0 * PI );
    gluLookAt( viewPoint.x, viewPoint.y, viewPoint.z,
               0.0, 0.0, 0.0,
               0.0, 1.0, 0.0 );

    /* drawing stuff */
    //glEnable(GL_DEPTH_TEST);
    //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_TRIANGLES);
    // draw left turbine
    FOR(I,0,SZ(m_leftTurbine.TriList)) {
        Triangle3f &triL = m_leftTurbine.TriList[I];
        if(m_leftTurbine.TriCollision[I]) {
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m_colorCollision.cell);
            glColor3fv(m_colorCollision.cell);
        }
        else {
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m_colorCollisionFree.cell);
            glColor3fv(m_colorCollisionFree.cell);
        }
        FOR(J,0,3)
            glVertex3fv(triL.cell[J].cell);
    }
    // draw right turbine
    FOR(I,0,SZ(m_rightTurbine.TriList)) {
        Triangle3f &triR = m_rightTurbine.TriList[I];
        if(m_rightTurbine.TriCollision[I]) {
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m_colorCollision.cell);
            glColor3fv(m_colorCollision.cell);
        }
        else {
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, m_colorCollisionFree.cell);
            glColor3fv(m_colorCollisionFree.cell);
        }
        FOR(J,0,3)
            glVertex3fv(triR.cell[J].cell);
    }
    glEnd();
    
    return TRUE;
}

llib::Boolean TurbineSimulator::OnSpecialKeyDown( Uint8 key, Int32 x, Int32 y, Uint8 modifiers )
{
    switch(key) {
        case GLUT_KEY_UP:
            if(m_viewPointDistance - m_viewPointDistanceDelta >= m_viewPointDistanceMin)
                m_viewPointDistance -= m_viewPointDistanceDelta;
            break;
        case GLUT_KEY_DOWN:
            if(m_viewPointDistance + m_viewPointDistanceDelta <= m_viewPointDistanceMax)
                m_viewPointDistance += m_viewPointDistanceDelta;
            break;
        case GLUT_KEY_LEFT:
            m_viewPointAngle -= m_viewPointAngleDelta;
            if(m_viewPointAngle < 0) m_viewPointAngle += 360.0;
            break;
        case GLUT_KEY_RIGHT:
            m_viewPointAngle += m_viewPointAngleDelta;
            if(m_viewPointAngle >= 360.0) m_viewPointAngle -= 360.0;
            break;
        default:
            break;
    }

    return TRUE;
}

llib::Boolean TurbineSimulator::Initialize()
{
    m_timer.restart();
    m_lastTime = Float(m_timer.elapsed());
    m_stepId = 0;
    return TRUE;
}

TurbineSimulator::TurbineSimulator():
    m_viewPointAngle(0.0),
    m_viewPointAngleDelta(1.0),
    m_viewPointDistance(15.0),
    m_viewPointDistanceDelta(0.5),
    m_viewPointDistanceMax(30.0),
    m_viewPointDistanceMin(0.5),
    m_viewPointHeight(8.0),
    m_timeDelta(0.025),
    m_stepId(0),
    m_leftTurbine(Vector3f(-5.f,0.5f,0.f),4.5f,28,25.f,-45.f),
    m_rightTurbine(Vector3f(5.f,-0.5f,0.f),4.5f,34,-15.f,225.f),
    m_colorCollisionFree(Color(0.2f,1.f,0.2f)),
    m_colorCollision(Color(1.f,0.2f,0.2f)),
    m_pause(TRUE),
    m_pausedTime(0.f)
{}

llib::Boolean TurbineSimulator::OnKeyDown( Uint8 key, Int32 x, Int32 y, Uint8 modifiers )
{
    switch(key) {
        case 32:    /* space */
            if(m_pause) {
                m_pause = FALSE;
            } else {
                m_pause = TRUE;
                m_pausedTime = Float(m_timer.elapsed()) - m_lastTime;
            }
            break;
        default:
            break;
    }
    return TRUE;
}

TurbineModel::TurbineModel( Vector3f center, Float radius, Int32 fanNum, Float angularVelocity, Float startAngle ):
    m_Center(center),
    m_Radius(radius),
    m_FanNum(fanNum),
    m_AngularVelocity(angularVelocity),
    TriList( vector<Triangle3f>(m_FanNum) ),
    TriCollision( vector<Boolean>(m_FanNum) ),
    m_StartAngle(startAngle)
{
    update(0.f);
}

void TurbineModel::update( Float time )
{
    // current spin angle
    Float angle = m_AngularVelocity * time;
    angle += m_StartAngle;
    angle -= (int)(angle / 360) * 360;
    angle = angle / 180 * PI;
    // angle between neighboring fans
    Float fanAngle = 2 * PI / m_FanNum;
    // the base fan triangle config
    const Float xSpan = 1.f, ySpan = 1.f;
    const Float p0_x  = -xSpan + m_Radius;
    const Float p12_x = xSpan + m_Radius;
    const Float p1_y  = ySpan, p2_y = -ySpan;
    // calculate fans
    FOR(I,0,m_FanNum) {
        Triangle3f &tri = TriList[I];
        Float sinVal = sin(angle);
        Float cosVal = cos(angle);
        // p0
        tri.cell[0].x = cosVal * p0_x;
        tri.cell[0].y = 0.f;
        tri.cell[0].z = sinVal * p0_x;
        // p1
        tri.cell[1].x = cosVal * p12_x;
        tri.cell[1].y = p1_y;
        tri.cell[1].z = sinVal * p12_x;
        // p2
        tri.cell[2].x = cosVal * p12_x;
        tri.cell[2].y = p2_y;
        tri.cell[2].z = sinVal * p12_x;
        // center translate
        FOR(v,0,3) 
            FOR(axis,0,3)
                tri.cell[v].cell[axis] += m_Center.cell[axis];
        // angle increase
        angle += fanAngle;
    }
}
